package ca.tatham.triplipse.views;

import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.dialogs.TrayDialog;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.StructuredViewer;
import org.eclipse.jface.viewers.ViewerSorter;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.part.ViewPart;

import ca.tatham.triplipse.Activator;
import ca.tatham.triplipse.RdfStatement;
import ca.tatham.triplipse.editors.RdfEditor;
import ca.tatham.triplipse.filter.RdfStatementFilterDialog;

public abstract class RdfDocumentView extends ViewPart {
	private static final String ICON_FILTER = "icons/filter.gif";//$NON-NLS-1$
	private static final String ICON_TOGGLE_PREFIX = "icons/togglePrefix.gif"; //$NON-NLS-1$

	private StructuredViewer m_viewer;
	private ViewerSorter m_sorter;
	private RdfDocumentContentProvider m_contentProvider;
	private IRdfStatementLabelProvider m_labelProvider;

	private TrayDialog m_filterDialog;
	private Action m_filterAction;
	private Action m_togglePrefixAction;
	private Action m_doubleClickAction;

	protected RdfDocumentView() {
		super();
	}

	protected abstract StructuredViewer createViewer(Composite parent);

	protected abstract ViewerSorter createSorter();

	protected abstract RdfDocumentContentProvider createContentProvider();

	protected abstract IRdfStatementLabelProvider createLabelProvider();

	@Override
	public void createPartControl(Composite parent) {
		m_viewer = createViewer(parent);
		m_contentProvider = createContentProvider();
		m_viewer.setContentProvider(m_contentProvider);
		m_labelProvider = createLabelProvider();
		m_labelProvider.addListener(m_contentProvider);
		m_viewer.setLabelProvider(m_labelProvider);
		m_sorter = createSorter();
		m_viewer.setSorter(m_sorter);
		m_viewer.setInput(getViewSite());
		makeActions();
		hookContextMenu();
		hookDoubleClickAction();
		contributeToActionBars();
	}

	/**
	 * Passing the focus request to the viewer's control.
	 */
	public void setFocus() {
		getViewer().getControl().setFocus();
	}

	public final StructuredViewer getViewer() {
		return m_viewer;
	}

	protected final ViewerSorter getSorter() {
		return m_sorter;
	}

	protected final RdfDocumentContentProvider getContentProvider() {
		return m_contentProvider;
	}

	protected final IRdfStatementLabelProvider getLabelProvider() {
		return m_labelProvider;
	}

	protected final void hookContextMenu() {
		MenuManager menuMgr = new MenuManager("#PopupMenu");
		menuMgr.setRemoveAllWhenShown(true);
		menuMgr.addMenuListener(new IMenuListener() {
			public void menuAboutToShow(IMenuManager manager) {
				fillContextMenu(manager);
			}
		});
		Menu menu = menuMgr.createContextMenu(getViewer().getControl());
		getViewer().getControl().setMenu(menu);
		getSite().registerContextMenu(menuMgr, getViewer());
	}

	private void contributeToActionBars() {
		IActionBars bars = getViewSite().getActionBars();
		fillLocalPullDown(bars.getMenuManager());
		fillLocalToolBar(bars.getToolBarManager());
	}

	private void fillLocalPullDown(IMenuManager manager) {
		manager.add(getFilterAction());
		manager.add(getTogglePrefixAction());
		fillLocalPullDownAdditional(manager);
	}

	protected abstract void fillLocalPullDownAdditional(IMenuManager menu);

	protected final void fillContextMenu(IMenuManager manager) {
		manager.add(getFilterAction());
		manager.add(getTogglePrefixAction());
		fillContextMenuAdditional(manager);
		manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
	}

	protected abstract void fillContextMenuAdditional(IMenuManager manager);

	private void fillLocalToolBar(IToolBarManager manager) {
		manager.add(getFilterAction());
		manager.add(getTogglePrefixAction());
		manager.add(new Separator());
		fillLocalToolBarAdditional(manager);
	}

	protected abstract void fillLocalToolBarAdditional(IToolBarManager manager);

	private void makeActions() {
		makeFilterAction();
		makeTogglePrefixAction();
		makeDoubleClickAction();
		makeAdditionalActions();
	}

	protected void makeAdditionalActions() {
		// optional method
	}

	protected Action getDoubleClickAction() {
		return m_doubleClickAction;
	}

	private void hookDoubleClickAction() {
		getViewer().addDoubleClickListener(new IDoubleClickListener() {
			public void doubleClick(@SuppressWarnings("unused")
			DoubleClickEvent event) {
				getDoubleClickAction().run();
			}
		});
	}

	private void makeDoubleClickAction() {
		m_doubleClickAction = new Action() {
			public void run() {

				IEditorPart activeEditor = getSite().getPage()
						.getActiveEditor();
				if (activeEditor != null && activeEditor instanceof RdfEditor) {
					ISelection selection = getViewer().getSelection();
					Object node = ((IStructuredSelection) selection)
							.getFirstElement();
					RdfStatement statement = getStatementFromSelection(node);

					if (statement != null) {
						((RdfEditor) activeEditor).setLineNumber(statement
								.getLineNumber() + 1);
						activeEditor.setFocus();
						getSite().getPage().activate(activeEditor);
					}

				}

			}

		};
	}

	protected abstract RdfStatement getStatementFromSelection(
			Object selectedObject);

	private void makeFilterDialog() {
		m_filterDialog = new RdfStatementFilterDialog(getViewer().getControl()
				.getShell(), getContentProvider(), getContentProvider()
				.getFilter());
	}

	protected TrayDialog getFilterDialog() {
		if (m_filterDialog == null) {
			makeFilterDialog();
		}
		return m_filterDialog;
	}

	protected Action getFilterAction() {
		return m_filterAction;
	}

	private void makeFilterAction() {
		m_filterAction = new Action() {
			public void run() {
				getFilterDialog().open();
			}
		};
		m_filterAction.setText("Configure Filters...");
		m_filterAction
				.setToolTipText("Configure the filters to be applied to this view");
		m_filterAction.setImageDescriptor(Activator
				.getImageDescriptor(ICON_FILTER));
	}

	protected Action getTogglePrefixAction() {
		return m_togglePrefixAction;
	}

	private void makeTogglePrefixAction() {
		m_togglePrefixAction = new Action("Toggle Prefix", IAction.AS_CHECK_BOX) {
			public void run() {
				boolean isPrefixOn = getLabelProvider().togglePrefix();
				getTogglePrefixAction().setChecked(isPrefixOn);
			}
		};
		m_togglePrefixAction.setToolTipText("Toggle Prefixing");
		m_togglePrefixAction.setImageDescriptor(Activator
				.getImageDescriptor(ICON_TOGGLE_PREFIX));
		m_togglePrefixAction.setChecked(getLabelProvider().isPrefixing());
	}

}
